/*******************************************************************************
 * Copyright (c) 2007, Geophysical Center RAS
 * Sergey Berezin, Dmitry Voitsekhovskiy
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms,
 * with or without modification, are permitted provided
 * that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * - Neither the name of the Geophysical Center RAS nor the names
 * of its contributors may be used to endorse or promote products
 * derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ******************************************************************************/
﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.ComponentModel;
using System.IO;
using VisualEsse.DataEngine.Types;
using DAI.ClientToolkit;
using System.Xml;
using System.Globalization;


namespace VisualEsse.DataEngine
{
    /// <summary>
    /// Retrieves the data as CDM from the Esse Services for any specified region.
    /// </summary>
    public class CDMDataSource : EsseBaseDataSource, IGrid2dDataSource
    {
        public CDMDataSource(DataProvider provider, EsseParameterDescription parameter, GeoPoint min, GeoPoint max, DateTime date)
            : base(provider, parameter, min, max, date)
        {
        }

        //protected ScalarArray2d[] ReadNcMLArrays(XmlReader xr)
        //{
        //    if (!xr.ReadToFollowing("netcdf"))
        //        throw new Exception("netcdf element not found");

        //    CdmDimension dimTime = new CdmDimension(xr);
        //    CdmDimension dimLat = new CdmDimension(xr);
        //    CdmDimension dimLon = new CdmDimension(xr);

        //    CdmVariable time = new CdmVariable(xr);
        //    CdmVariable lat = new CdmVariable(xr);
        //    CdmVariable lon = new CdmVariable(xr);
        //    CdmVariable vals = new CdmVariable(xr);

        //    float[] pdata = vals.ParseValues();
        //    if (pdata.Length != dimTime.Length * dimLat.Length * dimLon.Length)
        //        throw new Exception("Dimensions of variables do not match");

        //    ScalarArray2d[] resultData = new ScalarArray2d[dimTime.Length];
        //    for (int i = 0; i < resultData.Length; i++)
        //        resultData[i] = new ScalarArray2d(dimLon.Length, dimLat.Length);

        //    int timeIdx = 0;
        //    int latIdx = 0;
        //    int lonIdx = 0;
        //    int numDims = vals.Dimensions.Length;
        //    bool isLatAsc = lat.IsAscendencyOrder;

        //    foreach (float v in pdata)
        //    {
        //        if (isLatAsc)
        //            resultData[timeIdx][lonIdx, latIdx] = v;
        //        else
        //            resultData[timeIdx][lonIdx, dimLat.Length - latIdx - 1] = v;
        //        int dimIdx = numDims - 1;
        //        while (dimIdx >= 0)
        //        {
        //            if (vals.Dimensions[dimIdx] == dimLat.Name)
        //            {
        //                latIdx++;
        //                if (latIdx >= dimLat.Length)
        //                {
        //                    latIdx = 0;
        //                    dimIdx--;
        //                }
        //                else
        //                    break;
        //            }
        //            else if (vals.Dimensions[dimIdx] == dimLon.Name)
        //            {
        //                lonIdx++;
        //                if (lonIdx >= dimLon.Length)
        //                {
        //                    lonIdx = 0;
        //                    dimIdx--;
        //                }
        //                else
        //                    break;
        //            }
        //            else if (vals.Dimensions[dimIdx] == dimTime.Name)
        //            {
        //                timeIdx++;
        //                if (timeIdx >= dimTime.Length)
        //                {
        //                    timeIdx = 0;
        //                    dimIdx--;
        //                }
        //                else
        //                    break;
        //            }
        //            else
        //                throw new Exception("Unknown dimension " + vals.Dimensions[dimIdx] + " is values variable");
        //        }
        //    }
        //    return resultData;
        //}

        protected ScalarArray2d[] PerformRequest(EsseWebServiceDataProvider esseProvider, object[] activities)
        {
            string url = esseProvider.DataServiceUrl;
            DataService dai = new DataService(url);

            string resourceName = esseProvider.ResourceName;
            DataServiceResource esse = dai.DataServiceResource(resourceName);

            //Console.WriteLine("Activity list prepared. Starting service call...\n");
            DateTime start = DateTime.Now;
            XmlElement resp = esse.Perform(new PerformType(null, null, activities));

            DateTime stop = DateTime.Now;
            //Console.WriteLine("Service call completed. Wall clock elapsed time: {0}", stop.Subtract(start));

            XmlNamespaceManager ns = new XmlNamespaceManager(new NameTable());
            ns.AddNamespace("types", "http://ogsadai.org.uk/namespaces/2005/10/types");


            // Console.WriteLine(resp.OuterXml);
            string result = ((XmlElement)resp.SelectSingleNode("//types:result[@name='XmlDataCompressed" + "']", ns)).InnerText;

            XmlReader xr = XmlReader.Create(new System.IO.StreamReader(
                                   new System.IO.Compression.GZipStream(
                                       new System.IO.MemoryStream(Convert.FromBase64String(result)),
                                       System.IO.Compression.CompressionMode.Decompress)));

            NcmlGeoGridDocument doc = new NcmlGeoGridDocument();
            doc.Load(xr);
            return doc.GetData(doc.GetValuesVariables()[0].Name);
        }

        protected ScalarArray2d GetArrayFromSource(EsseWebServiceDataProvider esseProvider, EsseParameterDescription parameter, float lat0, float lat1, int latPoints, float lon0, float lon1, int lonPoints, DateTime dateTime)
        {
            string source = esseProvider.DataSourceString;

            // List of activities
            object[] activities = new object[2];

            // Prepare first activity
            object[] ap = new object[8];
            ap[0] = new GetCdmDataActivityTypeOutput("XmlData");
            ap[1] = new GetCdmDataActivityTypeValue("source", source);

            string dateTimeString = dateTime.ToString("s") + "UTC";

            ap[2] = new GetCdmDataActivityTypeValue("time", dateTimeString);
            ap[3] = new GetCdmDataActivityTypeValue("name", parameter.Name);
            ap[4] = new GetCdmDataActivityTypeValue("verticalCoord", parameter.VerticalCoord.Name);
            ap[5] = new GetCdmDataActivityTypeValue("level", parameter.Level.ToString());
            ap[6] = new GetCdmDataActivityTypeRange("lat",
                lat0.ToString(CultureInfo.InvariantCulture),
                lat1.ToString(CultureInfo.InvariantCulture),
                latStep.ToString(CultureInfo.InvariantCulture));
            ap[7] = new GetCdmDataActivityTypeRange("lon",
                lon0.ToString(CultureInfo.InvariantCulture),
                lon1.ToString(CultureInfo.InvariantCulture),
                lonStep.ToString(CultureInfo.InvariantCulture));


            activities[0] = new GetCdmDataActivityType("getDataActivity2"/*"DataToXml"*//* + parameter.EsseParameter.ToString()*/, ap);

            // Prepare second activity
            activities[1] = new GZIPCompressionType("gzip" /*+ parameter.EsseParameter.ToString()*/,
                "XmlData" /*+ parameter.EsseParameter.ToString()*/,
                "XmlDataCompressed" /*+ parameter.EsseParameter.ToString()*/,
               GZIPCompressionTypeSettingsChecksum.adler32);

            return PerformRequest(esseProvider, activities)[0];
        }

        protected override IScalarArray2d[] GetArraysFromSource(EsseWebServiceDataProvider esseProvider, EsseParameterDescription parameter, float lat0, float lat1, int latPoints, float lon0, float lon1, int lonPoints, DateTime dateTime, int stepInMin)
        {
            int count = 60 * 24 / stepInMin;
            ScalarArray2d[] r = new ScalarArray2d[count];
            DateTime currTime = dateTime;
            for (int i = 0; i < count; i++)
            {
                currTime = currTime.AddMinutes(stepInMin);
                r[i] = GetArrayFromSource(esseProvider, parameter, lat0, lat1, latPoints, lon0, lon1, lonPoints, currTime);
            }
            return r;

            /*
            // List of activities
            object[] activities = new object[2];

            // Prepare first activity
            object[] ap = new object[8];
            ap[0] = new GetCdmDataActivityTypeOutput("XmlData");
            ap[1] = new GetCdmDataActivityTypeValue("source", source);

            string fromString = dateTime.ToString("s") + "UTC";
            DateTime to = dateTime.AddDays((double)(24 * 60 - stepInMin) / (24 * 60));
            string toString = to.ToString("s") + "UTC";

            ap[2] = new GetCdmDataActivityTypeRange("time", fromString, toString, stepInMin.ToString());
            ap[3] = new GetCdmDataActivityTypeValue("name", parameter.Name);
            ap[4] = new GetCdmDataActivityTypeValue("verticalCoord", parameter.VerticalCoord.Name);
            ap[5] = new GetCdmDataActivityTypeValue("level", parameter.Level.ToString());
            ap[6] = new GetCdmDataActivityTypeRange("lat", 
                lat0.ToString(CultureInfo.InvariantCulture), 
                lat1.ToString(CultureInfo.InvariantCulture), 
                latStep.ToString(CultureInfo.InvariantCulture));
            ap[7] = new GetCdmDataActivityTypeRange("lon", 
                lon0.ToString(CultureInfo.InvariantCulture), 
                lon1.ToString(CultureInfo.InvariantCulture), 
                lonStep.ToString(CultureInfo.InvariantCulture));
            

            activities[0] = new GetCdmDataActivityType("getDataActivity2", ap);

            // Prepare second activity
            activities[1] = new GZIPCompressionType("gzip",
                "XmlData" ,
                "XmlDataCompressed" ,
               GZIPCompressionTypeSettingsChecksum.adler32);

            return PerformRequest(esseProvider, activities);*/
        }
    }
}
